package com.g3d.core;

import java.util.ArrayList;

import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLU;

import com.g3d.objects.G3DCamera;
import com.g3d.objects.G3DCamera.G3DENUM_CAMERA_TYPE;
import com.g3d.objects.G3DLight;
import com.g3d.objects.G3DLight.G3DENUM_LIGHT_TYPE;
import com.g3d.objects.G3DTexture;
import com.g3d.types.G3DColor;
import com.g3d.types.G3DVector3;

// class used to hold engine OpenGL states and change them if needed 
public class G3DState 
{
	private static GL10 _gl;
	
	
	private static boolean _multisampleEnabled = false;
	private static boolean _depthTestEnabled = false;
	private static boolean _ditherEnabled = false;
	private static boolean _lightEnabled = false;
	private static boolean _cullingEnabled = false;
	private static boolean _textureEnabled = false;
	private static boolean _texCoordArrayEnabled = false;
	private static boolean _blendEnabled = false;
	private static boolean _colorMaterialEnabled = false;
	
	// hold all available lights
	private static ArrayList<G3DLight> _lights = new ArrayList<G3DLight>();
	
	private static G3DColor _clearColor = new G3DColor(0.0f, 0.0f, 0.0f, 0.0f);
	
	private static int _lastTextureId = -1;
	private static int _shadeMode = -1;
	private static int _viewWidth = 1;
	private static int _viewHeight = 1;

	private static float _aspectRatio = 0;
	
	public static boolean vertexBufferObjectsSupported = false;
	public static boolean drawTextureSupported = false;
	public static boolean nonPowerOfTwoSupported = false;
	public static int maxLights; 
	
	//==============================================
	// TODO: state initialization functions
	//==============================================
	public static void g3dReset(GL10 gl)
	{
		G3DState._gl = gl;
	}
	public static void g3dInitialize()
	{
		G3DState.g3dGetCaps();
		// other needed implementations
	}
	private static void g3dGetCaps()
	{
		final String version = G3DState._gl.glGetString(GL10.GL_VERSION);
		final String renderer = G3DState._gl.glGetString(GL10.GL_RENDERER);
		final String extensions = G3DState._gl.glGetString(GL10.GL_EXTENSIONS);
 
		final boolean isOpenGL10 = version.contains("1.0");
		final boolean isOpenGL2X = version.contains("2.");
		final boolean isSoftwareRenderer = renderer.contains("PixelFlinger");
		final boolean isVBOCapable = extensions.contains("_vertex_buffer_object");
		final boolean isDrawTextureCapable = extensions.contains("draw_texture");
		final boolean isTextureNonPowerOfTwoCapable = extensions.contains("texture_npot");

		G3DState.vertexBufferObjectsSupported = !isSoftwareRenderer && (isVBOCapable || !isOpenGL10);
		G3DState.drawTextureSupported = isDrawTextureCapable || !isOpenGL10;
		G3DState.nonPowerOfTwoSupported = isTextureNonPowerOfTwoCapable || isOpenGL2X;
	}

	//==============================================
	// TODO: View matrix functions
	//==============================================
	public static void g3dCameraPresent(G3DCamera camera)
	{
		if(camera.getType() == G3DENUM_CAMERA_TYPE.CAMERA_PERSPECTIVE)
		{
			G3DState._gl.glMatrixMode(GL10.GL_PROJECTION);
			G3DState._gl.glLoadIdentity();
			GLU.gluOrtho2D(G3DState._gl, 0, G3DState._viewWidth, 0, G3DState._viewHeight);
			G3DState._gl.glMatrixMode(GL10.GL_MODELVIEW);
			G3DState._gl.glLoadIdentity();
		}
		else
		{
			G3DState._gl.glMatrixMode(GL10.GL_PROJECTION);
			G3DState._gl.glLoadIdentity();
			GLU.gluPerspective(G3DState._gl, camera.getFOV(), G3DState._aspectRatio, camera.getZNear(), camera.getZFar());					
			G3DState._gl.glMatrixMode(GL10.GL_MODELVIEW );
			G3DState._gl.glLoadIdentity();
			GLU.gluLookAt(G3DState._gl, 
					camera.getPosition().x, camera.getPosition().y, camera.getPosition().z, 
					camera.getDirection().x, camera.getDirection().y, camera.getDirection().z, 
					camera.getEyeUp().x, camera.getEyeUp().y, camera.getEyeUp().z);
		}
	}
	
	//==============================================
	// TODO: Getters setters
	//==============================================
	public static int g3dGetViewWidth()
	{
		return G3DState._viewWidth;
	}
	public static int g3dGetViewHeight()
	{
		return G3DState._viewHeight;
	}
	public static float g3dGetAspectRatio()
	{
		return G3DState._aspectRatio;
	}
	
	//==============================================
	// TODO: OpenGL setters
	//==============================================
	public static void g3dSetClearColor(G3DColor color)
	{
		if(G3DState._clearColor.compareTo(color) != 0)
		{
			G3DState._clearColor = color.clone();
		}
	}
	public static void g3dSetShadeMode(int shadeMode)
	{
		if(G3DState._shadeMode != shadeMode)
		{
			G3DState._shadeMode = shadeMode;
			G3DState._gl.glShadeModel(shadeMode);
		}
	}
	public static void g3dSetViewWidth(int width)
	{
		if(G3DState._viewWidth != width)
		{
			G3DState._viewWidth = width;
			G3DState.g3dCalculateAspectRatio();
		}
	}
	public static void g3dSetViewHeight(int height)
	{
		if(G3DState._viewHeight != height)
		{
			G3DState._viewHeight = height;
			G3DState.g3dCalculateAspectRatio();
		}
		
	}
	public static void setPerspectiveCorrectionHintFastest() 
	{
		G3DState._gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
	}
	private static void g3dCalculateAspectRatio()
	{
		G3DState._aspectRatio = (float)G3DState._viewWidth / (float)G3DState._viewHeight;
	}
	
	//==============================================
	// TODO: enable/disable OpenGL states 
	//==============================================
	public static void g3dColorMaterilaEnabled()
	{
		if(!G3DState._colorMaterialEnabled)
		{
			G3DState._colorMaterialEnabled = true;
			G3DState._gl.glEnable(GL10.GL_COLOR_MATERIAL);
		}
	}
	public static void g3dColorMaterilaDisable()
	{
		if(G3DState._colorMaterialEnabled)
		{
			G3DState._colorMaterialEnabled = false;
			G3DState._gl.glDisable(GL10.GL_COLOR_MATERIAL);
		}
	}
	public static void g3dBlendEnabled()
	{
		if(!G3DState._blendEnabled)
		{
			G3DState._blendEnabled = true;
			G3DState._gl.glEnable(GL10.GL_BLEND);
		}
	}
	public static void g3dBlendDisable()
	{
		if(G3DState._blendEnabled)
		{
			G3DState._blendEnabled = false;
			G3DState._gl.glDisable(GL10.GL_BLEND);
		}
	}
	public static void g3dDitherEnable()
	{
		if(!G3DState._ditherEnabled)
		{
			G3DState._ditherEnabled = true;
			G3DState._gl.glEnable(GL10.GL_DITHER);
		}
	}
	public static void g3dDitherDisable()
	{
		if(G3DState._ditherEnabled)
		{
			G3DState._ditherEnabled = false;
			G3DState._gl.glDisable(GL10.GL_DITHER);
		}
	}
	public static void g3dDepthTestEnable()
	{
		if(!G3DState._depthTestEnabled)
		{
			G3DState._depthTestEnabled = true;
			G3DState._gl.glEnable(GL10.GL_DEPTH_TEST);
		}
	}
	public static void g3dDepthTestDisable()
	{
		if(G3DState._depthTestEnabled)
		{
			G3DState._depthTestEnabled = false;
			G3DState._gl.glDisable(GL10.GL_DEPTH_TEST);
		}
	}
	public static void g3dMultisampleEnable()
	{
		if(!G3DState._multisampleEnabled)
		{
			G3DState._multisampleEnabled = true;
			G3DState._gl.glEnable(GL10.GL_MULTISAMPLE);
		}
	}
	public static void g3dMultisampleDisable()
	{
		if(G3DState._multisampleEnabled)
		{
			G3DState._multisampleEnabled = false;
			G3DState._gl.glDisable(GL10.GL_MULTISAMPLE);
		}
	}
	public static void g3dCullingEnable()
	{
		if(!G3DState._cullingEnabled)
		{
			G3DState._cullingEnabled = true;
			G3DState._gl.glEnable(GL10.GL_CULL_FACE);
		}
	}
	public static void g3dCullingDisable()
	{
		if(G3DState._cullingEnabled)
		{
			G3DState._cullingEnabled = false;
			G3DState._gl.glDisable(GL10.GL_CULL_FACE);
		}
	}
	public static void g3dLightEnable()
	{
		if(!G3DState._lightEnabled)
		{
			G3DState._lightEnabled = true;
			G3DState._gl.glEnable(GL10.GL_LIGHTING);
		}
	}
	public static void g3dLightDisable()
	{
		if(G3DState._lightEnabled)
		{
			G3DState._lightEnabled = false;
			G3DState._gl.glDisable(GL10.GL_LIGHTING);
		}
	}
	public static void g3dTextureEnabled()
	{
		if(!G3DState._textureEnabled)
		{
			G3DState._textureEnabled = true;
			G3DState._gl.glEnable(GL10.GL_TEXTURE_2D);
		}
	}
	public static void g3dTextureDisable()
	{
		if(G3DState._textureEnabled)
		{
			G3DState._textureEnabled = false;
			G3DState._gl.glDisable(GL10.GL_TEXTURE_2D);
		}
	}
	public static void g3dTexCoordArrayEnable()
	{
		if(!G3DState._texCoordArrayEnabled)
		{
			G3DState._texCoordArrayEnabled = true;
			G3DState._gl.glEnable(GL10.GL_TEXTURE_COORD_ARRAY);
		}
	}
	public static void g3dTexCoordArrayDisable()
	{
		if(G3DState._texCoordArrayEnabled)
		{
			G3DState._texCoordArrayEnabled = false;
			G3DState._gl.glDisable(GL10.GL_TEXTURE_COORD_ARRAY);
		}
	}

	//================================================
	// TODO: basic open gl calls
	//================================================
	public static void g3dClearColor()
	{
		G3DState._gl.glClearColor(G3DState._clearColor.r, G3DState._clearColor.g, G3DState._clearColor.b, G3DState._clearColor.a);
	}
	public static void g3dClear(int clearMask)
	{
		G3DState._gl.glClear(clearMask);
	}
	//=================================================================================
	// TODO: lighting calls present light class and can present just specific element
	//=================================================================================
	public static void g3dLightPresent(G3DLight light)
	{
		if(light.getIndex() < G3DState.maxLights)
		{
			if(light.getEnabled())
			{
				G3DState.g3dLightAmbientColor(light.getIndex(), light.getAmbientColor());	
				G3DState.g3dLightDiffuseColor(light.getIndex(), light.getDiffuseColor());
				G3DState.g3dLightSpecularColor(light.getIndex(), light.getSpecularColor());
				G3DState.g3dLightEmissiveColor(light.getIndex(), light.getEmissiveColor());
				
				G3DState.g3dLightPosition(light.getIndex(), light.getPosition());
				
				G3DState.g3dLightAttenutationX(light.getIndex(), light.getAttenuationX());
				G3DState.g3dLightAttenutationY(light.getIndex(), light.getAttenuationY());
				G3DState.g3dLightAttenutationZ(light.getIndex(), light.getAttenuationZ());
				
				G3DState.g3dLightSpotCutoff(light.getIndex(), light.getSpotCutoff());
				G3DState.g3dLightDirection(light.getIndex(), light.getDirection());
				G3DState.g3dLightSpotExponent(light.getIndex(), light.getSpotExponent());
			}
			
			G3DState.g3dLightIndexEnabled(light.getIndex(), light.getEnabled());
		}
		
	}
	public static void g3dLightIndexEnabled(int index, boolean enabled)
	{
		if(G3DState._lights.get(index).getEnabled() != enabled)
		{
			G3DState._lights.get(index).setEnabled(enabled);
			if(enabled)
				G3DState._gl.glEnable((GL10.GL_LIGHT0 + index));
			else
				G3DState._gl.glDisable((GL10.GL_LIGHT0 + index));
		}
	}
	public static void g3dLightAttenutationX(int index, float value)
	{
		if(G3DState._lights.get(index).getAttenuationX() != value)
		{
			G3DState._lights.get(index).setAttenuationX(value);
			G3DState._gl.glLightf(GL10.GL_LIGHT0 + index, GL10.GL_CONSTANT_ATTENUATION, value);
		}
	}
	public static void g3dLightDirection(int index, G3DVector3 direction)
	{
		if(G3DState._lights.get(index).getDirection().compareTo(direction) != 0)
		{
			G3DState._lights.get(index).setDirection(direction);
			G3DState._gl.glLightfv(GL10.GL_LIGHT0 + index, GL10.GL_SPOT_DIRECTION, direction.vectorArray(), 0);
		}
	}
	public static void g3dLightPosition(int index, G3DVector3 position)
	{
		if(G3DState._lights.get(index).getPosition().compareTo(position) != 0 && G3DState._lights.get(index).getType() == G3DENUM_LIGHT_TYPE.LIGHT_POSITIONAL)
		{
			G3DState._lights.get(index).setPosition(position);
			G3DState._gl.glLightfv(GL10.GL_LIGHT0 + index, GL10.GL_POSITION, position.vectorArray(1.0f), 0);
		}
	}
	public static void g3dLightAttenutationY(int index, float value)
	{
		if(G3DState._lights.get(index).getAttenuationY() != value)
		{
			G3DState._lights.get(index).setAttenuationY(value);
			G3DState._gl.glLightf(GL10.GL_LIGHT0 + index, GL10.GL_LINEAR_ATTENUATION, value);
		}
	}
	public static void g3dLightAttenutationZ(int index, float value)
	{
		if(G3DState._lights.get(index).getAttenuationZ() != value)
		{
			G3DState._lights.get(index).setAttenuationZ(value);
			G3DState._gl.glLightf(GL10.GL_LIGHT0 + index, GL10.GL_QUADRATIC_ATTENUATION, value);
		}
	}
	public static void g3dLightSpotCutoff(int index, float value)
	{
		if(G3DState._lights.get(index).getSpotCutoff() != value)
		{
			G3DState._lights.get(index).setSpotCutoff(value);
			G3DState._gl.glLightf(GL10.GL_LIGHT0 + index, GL10.GL_SPOT_CUTOFF, value);
		}
	}
	public static void g3dLightSpotExponent(int index, float value)
	{
		if(G3DState._lights.get(index).getSpotExponent() != value)
		{
			G3DState._lights.get(index).setSpotExponent(value);
			G3DState._gl.glLightf(GL10.GL_LIGHT0 + index, GL10.GL_SPOT_EXPONENT, value);
		}
	}
	public static void g3dLightAmbientColor(int index, G3DColor color)
	{
		if(G3DState._lights.get(index).getAmbientColor().compareTo(color) != 0)
		{
			G3DState._lights.get(index).setAmbientColor(color);
			G3DState._gl.glLightfv(GL10.GL_LIGHT0 + index, GL10.GL_AMBIENT, color.colorArray(), 0);
		}
	}
	public static void g3dLightDiffuseColor(int index, G3DColor color)
	{
		if(G3DState._lights.get(index).getDiffuseColor().compareTo(color) != 0)
		{
			G3DState._lights.get(index).setDiffuseColor(color);
			G3DState._gl.glLightfv(GL10.GL_LIGHT0 + index, GL10.GL_DIFFUSE, color.colorArray(), 0);
		}
	}
	public static void g3dLightSpecularColor(int index, G3DColor color)
	{
		if(G3DState._lights.get(index).getSpecularColor().compareTo(color) != 0)
		{
			G3DState._lights.get(index).setSpecularColor(color);
			G3DState._gl.glLightfv(GL10.GL_LIGHT0 + index, GL10.GL_SPECULAR, color.colorArray(), 0);
		}
	}
	public static void g3dLightEmissiveColor(int index, G3DColor color)
	{
		if(G3DState._lights.get(index).getEmissiveColor().compareTo(color) != 0)
		{
			G3DState._lights.get(index).setAmbientColor(color);
			G3DState._gl.glLightfv(GL10.GL_LIGHT0 + index, GL10.GL_EMISSION, color.colorArray(), 0);
		}
	}
	//==============================================
	// TODO: texture calls
	//==============================================
	public static int g3dGenerateTextureId()
	{
		int[] textureIds = new int[1];
	    G3DState._gl.glGenTextures(1, textureIds, 0);
	    return textureIds[0];
	}
	public static void g3dSetTextureParameters(int target, int parameter, float value)
	{
		G3DState._gl.glTexParameterf(target, parameter, value);
	}
	public static void g3dSetTextureEnvirament(int target, int parameter, float value)
	{
		G3DState._gl.glTexEnvf(target, parameter, value);
	}
	public static void g3dTexturePresent(G3DTexture texture)
	{
		if(G3DState._lastTextureId != texture.getTextureHardwareId())
		{
			G3DState._lastTextureId = texture.getTextureHardwareId();
			G3DState._gl.glBindTexture(GL10.GL_TEXTURE_2D, texture.getTextureHardwareId());
		}
	}
	public static void g3dForceTexturePresent(G3DTexture texture)
	{
		G3DState._lastTextureId = texture.getTextureHardwareId();
		G3DState._gl.glBindTexture(GL10.GL_TEXTURE_2D, texture.getTextureHardwareId());
	}
	public static void g3dTextureDelete(G3DTexture texture)
	{
		int[] textureIds = new int[1];
		textureIds[0] = texture.getTextureHardwareId();
		G3DState._gl.glDeleteTextures(1, textureIds, 0);
	}

}
